package com.yao2san.sim.storage.client.core.manager;

import com.yao2san.sim.storage.client.util.StorageUtil;
import com.yao2san.sim.storage.client.config.MinioProperties;
import com.yao2san.sim.storage.client.core.auth.Credentials;
import com.yao2san.sim.storage.client.core.enums.StorageType;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Item;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

public class MinioStorageManager extends AbstractStorageManager {
    private final MinioClient client;
    private final MinioProperties minioProperties;

    public MinioStorageManager(MinioClient client, MinioProperties minioProperties) {
        this.client = client;
        this.minioProperties = minioProperties;
    }

    @Override
    public boolean exist(String object) {
        try {
            client.statObject(StatObjectArgs.builder().bucket(minioProperties.getBucket()).object(object).build());
            return true;
        } catch (ErrorResponseException | InsufficientDataException |
                 InternalException | InvalidKeyException |
                 InvalidResponseException | IOException |
                 NoSuchAlgorithmException | ServerException |
                 XmlParserException e) {
            return false;
        }
    }

    @Override
    public void delete(String object) {

        RemoveObjectArgs args = RemoveObjectArgs.builder()
                .bucket(minioProperties.getBucket())
                .object(object).build();
        try {
            client.removeObject(args);
        } catch (ErrorResponseException | InsufficientDataException |
                 InternalException | InvalidKeyException |
                 InvalidResponseException | IOException |
                 NoSuchAlgorithmException | ServerException |
                 XmlParserException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void rename(String source, String target) throws IOException {
        copy(source, target, false);
    }

    @Override
    public void copy(String source, String target, boolean keepSource) throws IOException {
        if (source == null || source.isEmpty()) {
            throw new IllegalArgumentException("source object can not be null");
        }
        if (target == null || target.isEmpty()) {
            throw new IllegalArgumentException("target can not be null");
        }
        CopyObjectArgs args = CopyObjectArgs.builder().bucket(minioProperties.getBucket())
                .source(CopySource.builder().bucket(minioProperties.getBucket()).object(source).build())
                .object(target)
                .build();
        try {
            client.copyObject(args);

            if (!keepSource) {
                RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                        .bucket(minioProperties.getBucket())
                        .object(source)
                        .build();
                client.removeObject(removeObjectArgs);
            }
        } catch (ErrorResponseException | InsufficientDataException |
                 InternalException | InvalidKeyException |
                 InvalidResponseException | IOException |
                 NoSuchAlgorithmException | ServerException |
                 XmlParserException e) {
            throw new RuntimeException(e);
        }
    }


    @Override
    public List<StorageObject> list(String prefix) throws IOException {
        return list(prefix, null);
    }

    @Override
    public List<StorageObject> list(String prefix, String startAfter) throws IOException {
        ListObjectsArgs args = ListObjectsArgs.builder()
                .bucket(minioProperties.getBucket())
                .prefix(prefix)
                .delimiter("/")
                .startAfter(startAfter)
                .includeUserMetadata(true)
                .build();
        List<StorageObject> storageObjects = new ArrayList<>();
        Iterable<Result<Item>> objects = client.listObjects(args);
        objects.forEach(v -> {
            Item item;
            try {
                item = v.get();
                StorageObject storageObject = StorageObject.builder()
                        .object(item.objectName())
                        .size(item.size())
                        .lastModified(item.isDir() ? null : item.lastModified().toInstant().toEpochMilli())
                        .storageType(StorageType.MINIO.name())
                        .url(item.isDir() ? null : getUrl(item.objectName()))
                        .isDir(item.isDir())
                        .build();
                storageObjects.add(storageObject);
            } catch (ErrorResponseException | InsufficientDataException |
                     InternalException | InvalidKeyException |
                     InvalidResponseException | IOException |
                     NoSuchAlgorithmException | ServerException |
                     XmlParserException e) {
                throw new RuntimeException(e);
            }

        });
        return storageObjects;
    }

    private String getUrl(String object) {
        return this.minioProperties.getEndpoint()
                + StorageUtil.formatPath("/" + this.minioProperties.getBucket() + "/" + object);
    }

    @Override
    public String getName() {
        return StorageType.MINIO.name();
    }

    @Override
    public Credentials credentials(long durationSeconds) {
        return super.credentials(durationSeconds);
    }
}
